﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Collections;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace FUSE
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class MapGenerator : GameComponent
    {
        public Thread thread;
        private PerformanceCounter cpuPerformance;
        public List<Map> mapList;
        public int numberOfMaps = 5;
        
        public const int DEFAULT_NUMBER_COMPONENTS = 2;
        public const String DEFAULT_BACKGROUND = "mapBackground01";
        public const String DEFAULT_TEST_BACKGROUND = "TestBackground";
        public const String DEFAULT_START_PLANET = "startPlanet";
        public const String DEFAULT_END_PLANET = "endPlanet";
        public const String DEFAULT_PLANET = "Planet01Small";
        public const String DEFAULT_SUN = "star";
        public const String DEFAULT_WORMHOLE = "wormhole";
        public const String DEFAULT_BLACKHOLE = "bHoleSS";

        //map values to set
        public int numberPlanets = 0;
        public int numberSuns = 0;
        public int difficulty = 0;
        public int numberWormholes =0;
        public int numberBlackholes = 0;

        int wormholeAreaOfEffect = 10;

        private static Random randomGenerator;
        private Texture2D mapBackground;
        //private Hashtable galaxyComponents;
        private Texture2D componentTexture;
        private Texture2D sunTexture;
        private Texture2D wormholeTexture;
        private Texture2D blackholeTexture;

        private StartPlanet startPoint;
        private EndPlanet endPoint;



        public MapGenerator(Game game)
            : base(game)
        {
            this.startPoint = new StartPlanet();
            this.endPoint = new EndPlanet();

            randomGenerator = new Random();

            //this.galaxyComponents = new Hashtable();
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            mapList = new List<Map>();

            cpuPerformance = new PerformanceCounter();
            cpuPerformance.CategoryName = "Processor";
            cpuPerformance.CounterName = "% Processor Time";
            cpuPerformance.InstanceName = "_Total";




            ContentManager cm = Game.Content;

            this.componentTexture = cm.Load<Texture2D>(DEFAULT_PLANET);
            this.sunTexture = cm.Load<Texture2D>(DEFAULT_SUN);
            this.wormholeTexture = cm.Load<Texture2D>(DEFAULT_WORMHOLE);
            this.blackholeTexture = cm.Load<Texture2D>(DEFAULT_BLACKHOLE);
            this.mapBackground = cm.Load<Texture2D>(DEFAULT_BACKGROUND);

            this.startPoint.Initialize();
            this.startPoint.LoadContent(cm.Load<Texture2D>(DEFAULT_START_PLANET));
            this.startPoint.SetCenterPosition(mapBackground.Width / 2, mapBackground.Height);

            this.endPoint.Initialize();
            this.endPoint.LoadContent(cm.Load<Texture2D>(DEFAULT_END_PLANET));
            this.endPoint.SetCenterPosition(mapBackground.Width / 2, 0);

            thread = new Thread(new ThreadStart(ThreadedFunctions));
            thread.IsBackground = true;
            thread.Start();

            while (!thread.IsAlive) ;
        }

        private float getCpuUsage()
        {

            return cpuPerformance.NextValue();
        }

        public override void Update(GameTime gameTime)
        {
            float test = getCpuUsage();

            if (thread.IsAlive)
            {
                if (test > 80.0f)
                {
                    thread.Suspend();
                }

                if (test <= 80.0f && thread.ThreadState == System.Threading.ThreadState.Suspended)
                {
                    thread.Resume();
                }

            }
            base.Update(gameTime);
        }
       


        protected void ThreadedFunctions()
        {
            for (int i = 0; i < numberOfMaps;i++)
            {
                Map temp = GenerateMap();
                temp.galaxyForces = PhysicsEngine.createGravityGrid2(temp);

                //if(temp.valid = true)
                    mapList.Add(temp);


            }
        }
        

        public Map GenerateMap() // Will return a Map object
        {
            Map demoMap = new Map(mapBackground.Width, mapBackground.Height);

            Hashtable galaxyComponents = new Hashtable();

            demoMap.BackgroundTexture = this.mapBackground;
                

            GenericForce[,] forces = new GenericForce[mapBackground.Width, mapBackground.Height];
            startPoint.SetCenterPosition(mapBackground.Width / 2, mapBackground.Height);
            endPoint.SetCenterPosition(mapBackground.Width / 2, 0);

            galaxyComponents.Add("StartPlanet", startPoint);
            galaxyComponents.Add("EndPlanet", endPoint);
            for (int index = 0; index < numberSuns; index++)
            {
                Sun sun = new Sun(randomGenerator.Next(10), 10);
                sun.LoadContent(sunTexture);
                Vector2 sunPosition = generatePosition(sun.CrashCircle, galaxyComponents);
                sun.SetCenterPosition(sunPosition.X, sunPosition.Y);

                galaxyComponents.Add("x" + sunPosition.X + "y" + sunPosition.Y, sun);
            }
            for (int index = 0; index < numberBlackholes; index++)
            {
                Blackhole blackhole = new Blackhole(randomGenerator.Next(10));
                blackhole.LoadContent(blackholeTexture);
                Vector2 blackholePosition = generatePosition(blackhole.CrashCircle, galaxyComponents);

                blackhole.SetCenterPosition(blackholePosition.X, blackholePosition.Y);

                galaxyComponents.Add("x" + blackholePosition.X + "y" + blackholePosition.Y, blackhole);
            }
            for (int index = 0; index < numberPlanets; index++)
            {
                Planet planet = new Planet(randomGenerator.Next(10), 10);
                planet.LoadContent(componentTexture);
                Vector2 planetPosition = generatePosition(planet.OrbitCircle, galaxyComponents);

                planet.SetCenterPosition(planetPosition.X, planetPosition.Y);

                galaxyComponents.Add("x" + planetPosition.X + "y" + planetPosition.Y, planet);
            }

            for (int index = 0; index < numberWormholes; index++)
            {
                //Set the radius of the circular space around the worm holes
                Wormhole wormholeAlpha = new Wormhole(wormholeAreaOfEffect);
                Wormhole wormholeBeta = new Wormhole(wormholeAreaOfEffect);

                wormholeAlpha.LoadContent(wormholeTexture);
                wormholeBeta.LoadContent(wormholeTexture);
                
                //Associate sister wormholes, also position of circle will be set
                Vector2 wormholeAlphaPosition = generatePosition(wormholeAlpha.AreaOfEffect, galaxyComponents);
                Vector2 wormholeBetaPosition = generatePosition(wormholeBeta.AreaOfEffect, galaxyComponents);

                wormholeAlpha.sisterWormholePosition = wormholeBetaPosition;
                wormholeBeta.sisterWormholePosition = wormholeAlphaPosition;

                //Center wormhole positions
                wormholeAlpha.SetCenterPosition(wormholeAlphaPosition.X, wormholeAlphaPosition.Y);
                wormholeBeta.SetCenterPosition(wormholeBetaPosition.X, wormholeBetaPosition.Y);

                galaxyComponents.Add("x" + wormholeAlphaPosition.X + "y" + wormholeAlphaPosition.Y, wormholeAlpha);
                galaxyComponents.Add("x" + wormholeBetaPosition.X + "y" + wormholeBetaPosition.Y, wormholeBeta);
            }

            demoMap.galaxyComponentsTable = galaxyComponents;
            demoMap.galaxyForces = forces;

            return demoMap;
        }

        private Vector2 generatePosition(Circle circle, Hashtable gc)
        {
            int randomXCoord = randomGenerator.Next((int)circle.radius, (int)(mapBackground.Width -
                circle.radius));
            int randomYCoord = randomGenerator.Next((int)circle.radius, (int)(mapBackground.Height -
                circle.radius));

            circle.SetPosition(new Vector2(randomXCoord, randomYCoord));

            Boolean isTooClose = false;
            double distance = 0;

            while (!isTooClose)
            {

                foreach (DictionaryEntry item in gc)
                {
                    GalaxyComponent value = (GalaxyComponent)item.Value;

                    distance = value.ComponentBounds.DistanceFromCenter(circle);
                    if (value is MassedComponent)
                    {
                        //distance <= (((MassedComponent)value).SlingCircle.radius + circle.radius)
                        if (distance <= (((MassedComponent)value).SlingCircle.radius + circle.radius))
                        {
                            isTooClose = true;
                            break;
                        }
                    }
                    else
                    {
                        //distance <= ((((GalaxyComponent)value).ComponentBounds.radius * 2) + circle.radius)
                        if (distance <= ((((GalaxyComponent)value).ComponentBounds.radius) + circle.radius))
                        {
                            isTooClose = true;
                            break;
                        }
                    }
                }

                if (isTooClose == true)
                {
                    randomXCoord = randomGenerator.Next((int)circle.radius, (int)(mapBackground.Width -
                        circle.radius));
                    randomYCoord = randomGenerator.Next((int)circle.radius, (int)(mapBackground.Height -
                        circle.radius));

                    circle.SetPosition(new Vector2(randomXCoord, randomYCoord));

                    isTooClose = false;
                }
                else
                {
                    break;
                }
            }

            return new Vector2(randomXCoord, randomYCoord);
        }

       
    }
}
